﻿namespace WpfGenetic
{
    /// <summary>
    /// Возможные состояния особи
    /// </summary>
    public enum IndividualStatus
    {
        New,            // Только созданный
        Top,            // Лучшая особь, не участвует в турнирах
        Selected,       // Пережил минимум 1 отбор
        SelectedTop,    // Лучшая особь, не мутирует
        Mutation,       // Особь, которая мутировала
        Unknown,
    }

    /// <summary>
    /// Класс имеющий общие поля для всех шифров
    /// </summary>
    public abstract class Individual
    {
        // Частота биграмм
        public double FrequencyOfBigrams { get; set; }

        // Частота триграмм
        public double FrequencyOfTrigrams { get; set; }

        // Фитнесс функция
        public double Fitness { get; set; }

        // В каком поколении родилась данная особь
        public int Generation { get; set; } = 0;

        // Статус особи 
        public IndividualStatus Status { get; set; } = IndividualStatus.Unknown;
    }

    /// <summary>
    /// Дополнительные поля для шифра Виженера
    /// </summary>
    public class VigenereIndividual : Individual
    {
        // Значение ключа
        public string Key { get; set; } = string.Empty;

        // Длина ключа
        public Binary Length { get; set; } = null!;

        // Индекс совпадений
        public double IndexOfMatches { get; set; }
    }
    
    /// <summary>
    /// Дополнительные поля для перестановочного шифра
    /// </summary>
    public class PermutationIndividual : Individual
    {
        // Горизонтальный ключ
        public List<int> IntHorizontalKey { get; set; } = [];

        // Длина горизонтального ключа
        public Binary HorizontalLength { get; set; } = null!;

        // Вертикальный ключ
        public List<int> IntVerticalKey { get; set; } = [];

        // Длина вертикального ключа
        public Binary VerticalLength { get; set; } = null!;
    }

    /// <summary>
    /// Класс для представления десятичных чисел в двоичном виде
    /// </summary>
    public class Binary
    {
        private readonly List<bool> _bitArray;

        public Binary(int digit, int? maxLength = null) 
        {
            _bitArray = [];

            for(int i = digit; i > 0; i >>= 1)
            {
                _bitArray.Add(Convert.ToBoolean(i & 1));
            }

            if (maxLength is not null && maxLength > _bitArray.Count)
            {
                for (int i = (int)maxLength - _bitArray.Count; i > 0; i--)
                {
                    _bitArray.Add(false);
                }
            }
        }

        public Binary(List<bool> digit)
        {
            _bitArray = digit;
        }

        public bool this[int num]
        {
            get
            {
                if (_bitArray.Count > num)
                    return _bitArray[num];
                else
                    throw new ArgumentException("Выход за пределы Binary");
            }

            set
            {
                if (_bitArray.Count > num)
                    _bitArray[num] = value;
                else
                    throw new ArgumentException("Выход за пределы Binary");
            }
        }

        public int Count { get { return _bitArray.Count; } private set { } }

        public int GetInt()
        {
            int response = 0;

            int rank = 1;

            for ( int i = 0; i < this.Count; i++)
            {
                if (this[i])
                {
                    response += rank;
                }

                rank *= 2;
            }

            return response;
        }

        /// <summary>
        /// Определяет максимальное значение, которое может хранится в данном кол-ве бит
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        public static int GetMaxIntValue(int length)
        {
            int response = 0;

            int rank = 1;

            for (int i = 0; i < length; i++)
            {
                response += rank;

                rank *= 2;
            }

            return response;
        }

        /// <summary>
        /// Перемешивает два числа с вероятностью 50%
        /// </summary>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        public static Binary Shuffle(Binary one, Binary two)
        {
            if (one.Count != two.Count)
            {
                throw new InvalidOperationException("Разная размерность Binary");
            }

            Random rand = new(DateTime.UtcNow.Millisecond);

            List<bool> newBinary = [];

            for (int i = 0; i < one.Count; i++)
            {
                if (rand.Next(1, 101) <= 50)
                {
                    newBinary.Add(one[i]);
                }
                else
                {
                    newBinary.Add(two[i]);
                }
            }

            return new Binary(newBinary);
        }

        /// <summary>
        /// Прибавляет 1
        /// </summary>
        public void Inc()
        {
            for ( int i = 0; i < Count; i++)
            {
                if (_bitArray[i])
                {
                    _bitArray[i] = false;
                }
                else
                {
                    _bitArray[i] = true;
                    break;
                }

            }
        }

        /// <summary>
        /// Вычитает 1
        /// </summary>
        public void Dec()
        {
            for (int i = 0; i < Count; i++)
            {
                if (_bitArray[i])
                {
                    _bitArray[i] = false;
                    break;
                }
                else
                {
                    _bitArray[i] = true;
                }

            }
        }
    }
}
